package com.jlt.baidu.utils;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;

/**
 * 图像处理工具类
 * 
 * @author 苹果
 * @date 2019/12/24
 */
@Slf4j
public class Opencv420Util {
    /**
     * 读取图片转化成mat的操作对象
     * 
     * @param filename
     * @return
     */
    public static Mat imagerLoad(String filename) {
        return Imgcodecs.imread(filename);
    }

    /**
     * 读取图片，直接灰度化，
     * 
     * @param filename
     * @return
     */
    public static Mat imagerLoadGray(String filename) {
        // Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
        Mat img = imagerLoad(filename);
        return imageGray(img);
    }

    /**
     * 
     * 图片灰度化
     * 
     * @param img
     * @return
     */
    public static Mat imageGray(Mat img) {
        Mat gray = img.clone();
        Imgproc.cvtColor(img, gray, Imgproc.COLOR_BGR2GRAY);
        return gray;
    }

    /**
     * 图片的边缘检测
     * 
     * @param source
     * @param size
     * @return
     */
    public static Mat imageCanny(Mat source, double threshold1, double threshold2) {
        Mat cann = source.clone();
        Imgproc.Canny(source, cann, threshold1, threshold2);
        return cann;
    }

    /**
     * 保存图片至相应路径
     * 
     * @param filename
     * @param target
     */
    public static void imageSave(String filename, Mat target) {
        Imgcodecs.imwrite(filename, target);
    }

    /**
     * 压缩图片
     *
     * @param srcImagePath 源图片路径
     * @param desImagePath 目标路径
     * @param scale 压缩率
     * @throws IOException the io exception
     */
    public static void thumbnail(String srcImagePath, String desImagePath, double scale) {
        try {
            Thumbnails.of(srcImagePath).scale(scale).toFile(desImagePath);
        } catch (IOException e) {
            log.error("图像增强比例错误；", e);
        }
    }

    /**
     * 图片缩放，返回缩放后的对象，三倍缩小尺寸
     * 
     * @param obj
     * @return
     */
    @Deprecated
    public static Mat imageZoom(Mat obj) {
        Mat dst = new Mat();
        Imgproc.resize(obj, dst, new Size(obj.cols() / 3, obj.rows() / 3), 0, 0, Imgproc.INTER_AREA);
        return dst;
    }

    /**
     * 针对OCR业务的缩放，确保大于1000，修改尺寸
     * 
     * @param obj
     * @return
     */
    public static Mat imageZoomForOCR(Mat obj, int length, int width) {
        Mat dst = new Mat();
        Imgproc.resize(obj, dst, new Size(obj.cols() / length, obj.rows() / width), 0, 0, Imgproc.INTER_AREA);
        return dst;
    }

    /**
     * 图片放大，返回缩放后的对象,修改尺寸
     * 
     * @param obj
     * @return
     */
    public static Mat imageEnlarge(Mat obj, int length, int width) {
        Mat endst = new Mat();
        Imgproc.resize(obj, endst, new Size(obj.cols() * length, obj.rows() * width), 0, 0, Imgproc.INTER_LINEAR);
        return endst;
    }

    /**
     * 针对ocr的图片识别，尺寸1000X1000，压缩尺寸
     * 
     * @param obj
     */
    @Deprecated
    public static int[] ocrImageZoomRegex(Mat obj) {
        int cols = obj.cols() > 1000 ? obj.cols() / 1000 : 1;
        int rows = obj.rows() > 1000 ? obj.rows() / 1000 : 1;
        return new int[] {cols, rows};
    }

    /**
     * 针对ocr的图片识别，尺寸1000X1000，放大尺寸
     * 
     * @param obj
     */
    @Deprecated
    public static int[] ocrImageEnlargeRegex(Mat obj) {
        int cols = obj.cols() < 1000 ? obj.cols() * 10 / 1000 : 1;
        int rows = obj.rows() < 1000 ? obj.rows() * 10 / 1000 : 1;
        return new int[] {cols, rows};
    }

    /**
     * mat转换成base64对象
     * 
     * @param obj
     * @param type
     * @return
     */
    public static byte[] matToByte(Mat obj, String type) {
        MatOfByte mob = new MatOfByte();
        Imgcodecs.imencode("." + type, obj, mob);
        return mob.toArray();
    }

    /**
     * mat转换成BufferedImage对象
     * 
     * @param obj
     * @param type
     * @return
     */
    public static BufferedImage matToBuffer(Mat obj, String type) {
        MatOfByte mob = new MatOfByte();
        Imgcodecs.imencode("." + type, obj, mob);
        byte[] byteArray = mob.toArray();
        BufferedImage bufImage = null;
        try {
            InputStream in = new ByteArrayInputStream(byteArray);
            bufImage = ImageIO.read(in);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bufImage;
    }

    /**
     * 字节流转化mat
     * 
     * @param bytes
     * @param length
     * @param width
     */
    public static Mat byteToMat(byte[] bytes) {
        Mat target = new Mat(bytes.length, 1, CvType.CV_8SC1);
        target.put(0, 0, bytes);
        return target;
    }


    /**
     * 索贝尔处理，计算出图片的边缘检测的一个离散的一阶差分算子，也就是圈出 图片内容轮廓，沿X轴方向
     * 
     * @param mat
     * @param sobel
     */
    public static Mat SobelX(Mat mat) {
        Mat sobel = mat.clone();
        // 一样的效果
        // Imgproc.Sobel(dst, dstx, CvType.CV_16S, 1, 0, 3, 1, 1, Core.BORDER_DEFAULT);
        Imgproc.Sobel(mat, sobel, CvType.CV_8U, 1, 0);
        return sobel;
    }

    /**
     * 索贝尔处理，计算出图片的边缘检测的一个离散的一阶差分算子，也就是圈出 图片内容轮廓，沿Y轴方向
     * 
     * @param mat
     * @param sobel
     */
    public static Mat SobelY(Mat mat) {
        Mat sobel = mat.clone();
        // 一样的效果
        // Imgproc.Sobel(dst, dsty, CvType.CV_16S, 0, 1, 3, 1, 1, Core.BORDER_DEFAULT);
        Imgproc.Sobel(mat, sobel, CvType.CV_8U, 0, 1);
        return sobel;
    }

    /**
     * 索贝尔处理,合并x,y方向，边缘检测
     * 
     * @param mat
     */
    public static Mat Sobel(Mat mat) {
        Mat result = new Mat();;
        Mat x = SobelX(mat);
        Mat y = SobelY(mat);
        Core.addWeighted(x, 1, y, 1, 0, result);
        return result;
    }

    /**
     * 去除红色印章，蓝色印章
     * 
     * @param obj
     */
    public static Mat cancleRedAndblueConten(Mat obj) {
        // 去除红色印章
        List<Mat> channels = new ArrayList<>();
        // 用原图的去识别红色
        Core.split(obj, channels);
        if (channels.size() == 0) {
            return obj;
        }
        Mat blue = channels.get(0);
        // Mat green = channels.get(1);
        Mat red = channels.get(2);
        Mat red_binary = new Mat();
        Imgproc.threshold(red, red_binary, 200, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
        Imgproc.threshold(blue, red_binary, 200, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
        return red_binary;
    }

    /**
     * 图片二值化,黑底白字
     * 
     * @param source
     * @return
     */
    public static Mat thresholdBlackGround(Mat source) {
        Mat target = new Mat();
        Imgproc.adaptiveThreshold(source, target, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 5, 0);
        return target;
    }

    /**
     * 图片二值化,白底黑字
     * 
     * @param source
     * @param keysize 二值化系数
     * @return
     */
    public static Mat thresholdWhiteGround(Mat source, int keysize) {
        Mat target = new Mat();
        // 大幅去除二层的背景
        Imgproc.threshold(source, target, keysize, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
        // 不去除背景，适合彩色图片
        // Imgproc.threshold(source, target, keysize, 255, Imgproc.THRESH_TOZERO_INV | Imgproc.THRESH_TRUNC);
        return target;
    }

    /**
     * 二值化后图片降噪，均值滤波,以3X3最标准
     * 
     * @param source
     * @return
     */
    public static Mat averageBlur(Mat source) {
        Mat target = source.clone();
        Imgproc.blur(source, target, new Size(3, 3));
        return target;
    }

    /**
     * 二值化后图片降噪，高斯滤波
     * 
     * @param source
     * @return
     */
    public static Mat gaussianBlur(Mat source) {
        Mat target = source.clone();
        Imgproc.GaussianBlur(source, target, new Size(3, 3), 0, 0, Core.BORDER_DEFAULT);
        return target;
    }

    /**
     * 二值化后图片降噪，中值滤波，参数越低越接近原图，1最低
     * 
     * @param source
     * @return
     */
    public static Mat medianBlur(Mat source) {
        Mat target = source.clone();
        Imgproc.medianBlur(source, target, 1);
        return target;
    }

    /**
     * 扩展图片的边缘，用白色填充
     * 
     * @param source
     * @param cient 放大的系数(0,1]
     * @return
     */
    public static Mat extendImageBorder(Mat source, double cient) {
        if (cient < 0) {
            cient = 0;
        }
        Mat dst = new Mat();
        int top = (int) (source.rows() * cient);
        int bottom = (int) (source.rows() * cient);
        int left = (int) (source.cols() * cient);
        int right = (int) (source.cols() * cient);
        Core.copyMakeBorder(source, dst, top, bottom, left, right, Core.BORDER_REPLICATE);
        return dst;
    }

    /**
     * 增强对比度 有待进一步测试
     *
     * @param src BGR格式图像
     * @return
     */
    public static Mat histEqualize(Mat src) {
        Mat dst = src.clone();
        Imgproc.cvtColor(dst, dst, Imgproc.COLOR_BGR2YCrCb);
        List<Mat> list1 = new ArrayList<>();
        Core.split(dst, list1);
        Imgproc.equalizeHist(list1.get(0), list1.get(0));
        Core.normalize(list1.get(0), list1.get(0), 0, 255, Core.NORM_MINMAX);
        Core.merge(list1, dst);
        Imgproc.cvtColor(dst, dst, Imgproc.COLOR_YCrCb2BGR);
        return dst;
    }

    /**
     * 用于整体偏暗图像的增强,变亮 有待进一步测试
     *
     * @param src
     * @return
     */
    public static Mat laplaceEnhance(Mat src) {
        Mat srcClone = src.clone();
        float[] kernel = {0, 0, 0, -1, 5f, -1, 0, 0, 0};
        Mat kernelMat = new Mat(3, 3, CvType.CV_32FC1);
        kernelMat.put(0, 0, kernel);
        Imgproc.filter2D(srcClone, srcClone, CvType.CV_8UC3, kernelMat);
        return srcClone;
    }

    /**
     * 有待进一步测试
     * 
     * 对数变换可以将图像的低灰度值部分扩展，显示出低灰度部分更多的细节，
     * 
     * 将其高灰度值部分压缩，减少高灰度值部分的细节，从而达到强调图像低灰度部分的目的。
     *
     * @param src
     * @return
     */
    public static Mat logEnhance(Mat src) {
        Mat srcClone = src.clone();
        Mat imageResult = new Mat(srcClone.size(), CvType.CV_32FC3);
        Core.add(srcClone, new Scalar(5, 5, 5), srcClone);
        srcClone.convertTo(srcClone, CvType.CV_32F);
        Core.log(srcClone, imageResult);
        Core.normalize(imageResult, imageResult, 0, 255, Core.NORM_MINMAX);
        Core.convertScaleAbs(imageResult, imageResult);
        return imageResult;
    }

    /**
     * 从图片中，水平投影出最大矩形区域，形成文字识别区域
     * 
     * @param source
     */
    public static void projectionRect(Mat source) {

    }

    public static void main(String[] args) throws IOException {
        // String opencv420 =
        // "D:\\workSpace\\singleWorkSpace\\jlt-baidu\\src\\main\\resources\\opencv\\java\\x64\\opencv_java412.dll";
        // System.load(opencv420);
        // System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        // String fileBlack_sobel = "F:\\workSpace\\OCR\\suyue\\jingdong\\0c9517750af445848ca6f8097b448beb_wps222.jpg";
        //
        // Mat res = imagerLoad(fileBlack_sobel);
        // Mat endst = imageZoom(res);
        // Imgcodecs.imwrite("D:\\workSpace\\ocr\\tianma\\daohuo\\26ad1f5c419979b66a1b56700bc4f31_small.jpg", endst);
        thumbnail("F:\\workSpace\\OCR\\suyue\\tianmao\\effc9fd5f263456cfb010fabba24a76.jpg",
                "F:\\workSpace\\OCR\\suyue\\tianmao\\effc9fd5f263456cfb010fabba24a76_11.jpg", 0.8);
    }
}
